// Ofri Keidar 302933981 group 05

package model;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.LinkedList;

import model.apartment.*;
import model.building.Building;
import model.database.DBException;
import model.database.Database;

/**
 * Manages creating the apartments (and buildings) and storing them for long-term
 */
public class ApartmentsManager implements Model {
	
	private Map<String, Apartment.ParameterChecker> parameterChecker;
	
	private Database database; // system's database
	
	/**
	 * Constructor- creates a new object to manage the apartments and buildings
	 * @param database- database object to be used
	 * @throws DBException- throws database exception if could not initialize system
	 */
	public ApartmentsManager(Database database) throws DBException {
		
		// allocate a new database
		this.database = database;
		
		// allocate checker for apartment's parameters
		parameterChecker = new HashMap<String, Apartment.ParameterChecker>();
		
		// add checker for each apartment type
		parameterChecker.put(ParameterParser.FLAT, new Flat.FlatParameterChecker());
		parameterChecker.put(ParameterParser.GARDEN_APARTMENT, new GardenApartment.GardenApartmentParameterChecker());
		parameterChecker.put(ParameterParser.PENTHOUSE, new Penthouse.PenthouseParametersChecker());
	}

	/**
	 * Uses database to find all vacant flats in system.
	 * Flats are sorted by total area in ascending order
	 * Throws database exception there was a problem with the database connection
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Flat> getVacantFlats() throws DBException {
		
		// return all specified flats
		return (List)database.getVacantApartments(ParameterParser.FLAT);
	}
	
	/**
	 * Uses database to find all vacant flats with given room number in system.
	 * Flats are sorted by total area in ascending order
	 * Throws database exception there was a problem with the database connection
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Flat> getVacantFlats(double roomNum) throws DBException {
		
		// return all specified flats
		return (List)database.getVacantApartments(ParameterParser.FLAT, roomNum);
	}

	/**
	 * Uses database to find all vacant flats with given room number
	 * at given address.
	 * Flats are sorted by total area in ascending order
	 * Throws database exception there was a problem with the database connection
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Flat> getVacantFlats(double roomNum, String stName, int bldNumber)
			throws DBException {

		// get building at given address
		Building building = database.getBuildingByAddress(stName, bldNumber);
		
		// check if such building exists
		if (building == null) {
			
			// no building at given address
			return new LinkedList<Flat>();
		}
		
		// return all specified flats
		return (List)database.getVacantApartments(ParameterParser.FLAT, roomNum, building.getID());
	}
	
	/**
	 * Uses database to find all flats at given address in system.
	 * Flats are sorted by total area in ascending order
	 * Throws database exception there was a problem with the database connection
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Flat> getFlats(String stName, int bldNumber) throws DBException {
		
		// get building at given address
		Building building = database.getBuildingByAddress(stName, bldNumber);
		
		// check if such building exists
		if (building == null) {
			
			// no building at given address
			return new LinkedList<Flat>();
		}
		
		// return all specified flats
		return (List)database.getApartments(ParameterParser.FLAT, building.getID());
	}
	
	/**
	 * Uses database to find all flats in system.
	 * Flats are sorted by total area in ascending order
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Flat> getAllFlats() throws DBException {
		
		// return all flats
		return (List)database.getApartments(ParameterParser.FLAT);
	}

	/**
	 * Uses database to find all penthouses in system.
	 * Penthouses are sorted by total area in ascending order
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<Penthouse> getAllPenthouses() throws DBException {
		
		// return all penthouses
		return (List)database.getApartments(ParameterParser.PENTHOUSE);
	}
	
	/**
	 * Uses database to find all garden apartments in system.
	 * Garden apartments are sorted by total area in ascending order
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<GardenApartment> getAllGardenAprtmnts() throws DBException {
		
		// return all garden apartments
		return (List)database.getApartments(ParameterParser.GARDEN_APARTMENT);
	}
	
	/**
	 * Uses database to find building at given address
	 */
	@Override
	public Building getBuilding(String stName, int bldNumber)
			throws DBException {
		
		// return building with given address
		return database.getBuildingByAddress(stName, bldNumber);
	}	
	
	/**
	 * Uses database to find all vacant apartments in system.
	 * Apartments are sorted by total area in ascending order
	 */
	public List<Apartment> getVacantApartments() throws DBException {
		
		// return all vacant apartments
		return database.getVacantApartments();
	}
	
	/**
	 * Uses database to find all vacant apartments at given address.
	 * Apartments are sorted by total area in ascending order
	 */
	@Override
	public List<Apartment> getVacantApartments(String stName, int bldNumber)
			throws DBException {
		
		// get building at given address
		Building building = database.getBuildingByAddress(stName, bldNumber);
		
		// check if such building exists
		if (building == null) {
			
			// no building at given address
			return new LinkedList<Apartment>();
		}
		
		// return vacant apartments in the building
		return database.getVacantApartments(building.getID());
	}
	
	/**
	 * Activates parameter parser of given apartment type and
	 * returns its result
	 */
	@Override
	public boolean checkApartmentsParameters(String apartmentType,
			Map<String, String> parameters) {
		
		// activate relevant apartment's parameter checker
		return parameterChecker.get(apartmentType).parseParameters(parameters);
	}
	
	/**
	 * Gets list of types for new building's apartments, list of maps of apartments' parameters and new building's address.
	 * Creates new apartments with given details and a building at given address holding those apartments.
	 * Adds the new building to the database.
	 * Returns True if could add the building, False if not
	 */
	@Override
	public boolean addNewBuilding(List<String> apartmentTypes,
			List<Map<String, String>> parameterList, String stName,
			int bldNumber) throws DBException {
		
		// get number of new building's apartments
		int numOfAparments = parameterList.size();
		
		// allocate apartment array for the new building
		Apartment[] apartments = new Apartment[numOfAparments];
		
		// use an apartment factory to create all apartments
		ApartmentFactory factory = new ApartmentFactory(); 
		
		// calculate new building's ID
		int bldID = getNextBuildingID();
		
		// create apartments according to given details
		for (int i = 0; i < numOfAparments; ++i) {

			apartments[i] = factory.create(apartmentTypes.get(i), parameterList.get(i), bldID, bldNumber + " " + stName);
		}
		
		// create a new building with these apartments at given address
		Building newBuilding = new Building(stName, bldNumber, apartments, bldID);
				
		// add the new building to the database
		return database.addBuilding(newBuilding);
	}
	
	/**
	 * Returns ID of next building.
	 * The ID is number-of-buildings plus 1
	 * @return ID of next building
	 * @throws DBException- throws database exception if could not communicate with database
	 */
	private int getNextBuildingID() throws DBException {
	
		// return next ID
		return database.countBuildings()+1;
	}

	/**
	 * Uses the database to update the new resident name of the apartment matching given number
	 * at given address.
	 * Returns True if could update the apartment, False if not
	 */
	@Override
	public boolean updateResident(String stName, int bldNumber,
			int apartmentNum, String newResident) throws DBException {
		
		// get building with given address
		Building buildingToUpdate = database.getBuildingByAddress(stName, bldNumber);
		
		// check if such building exists
		if (buildingToUpdate == null) {
			
			// no such building in system
			return false;
		}
		
		// update resident name of specified apartment
		return database.updateResident(newResident, buildingToUpdate.getID(), apartmentNum);
	}
}